package com.googlecode.skald.server.demo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * This simple game world is provided for demonstration purposes.
 * <p>
 * This world include a ball, a dog, and a player character. 
 * 
 * @author Zach Tomaszewski
 */
public class GameWorld {
  
  protected static Map<String, WorldObject> objs = 
      new HashMap<String, WorldObject>(); 
  protected static WorldObject pc;  //current player character
  
  /**
   * Performs the given action in the world.  Assumes the player character
   * is the actor performing the action.  Returns the narration (as HTML)
   * that results.
   */
  public static String doAction(Action action, 
                                WorldObject dobj, WorldObject iobj) {
    StringBuilder outcome = new StringBuilder();
    try {
      outcome.append(pc.actorOf(action, dobj, iobj));
      if (iobj != null) {
        outcome.append(iobj.iobjOf(action, dobj, iobj));
      }
      if (dobj != null) {
        outcome.append(dobj.dobjOf(action, dobj, iobj));
      }
    }catch (ActionAbortedException e) {
      outcome.append(e.getNarration());
    }
    return outcome.toString(); 
  }
                                
  
  
  /** Execute the given cmd, returning the narration that results. */
  public static String doCmd(String cmd) {
    String[] tokens = cmd.split("\\s+");
    if (tokens.length > 3) {
      return p("ERROR: Cmd string is longer than 3 tokens: " + cmd);
    }
    Action act = null;
    try {
      act = Enum.valueOf(Action.class, tokens[0].toUpperCase());
    }catch (IllegalArgumentException e) {
      return p("ERROR: Unrecognized action verb in cmd: " + cmd);
    }
    String dobjName = (tokens.length > 1) ? tokens[1] : null;
    String iobjName = (tokens.length > 2) ? tokens[2] : null;
    if (dobjName != null && !objs.containsKey(dobjName) || 
        iobjName != null && !objs.containsKey(iobjName)) {
      return p("ERROR: Unrecognized object names in cmd: " + cmd);
    }
    WorldObject dobj = objs.get(dobjName);
    WorldObject iobj = objs.get(iobjName);
    return doAction(act, dobj, iobj);
  }
  
  /**
   * Returns a JSON list of affordances given the current world state.
   */
  public static String getAffordances() {
    StringBuilder affords = new StringBuilder();
    affords.append("[");
    Collection<WorldObject> wobjs = objs.values();
    for (Action act : Action.values()) {
      // we would normally check if actor can perform each action here, but
      // this demo world assumes that actor is always the PC and the PC is
      // always capable of all actions
      switch (act.getArity()) {
        case 0:
          affords.append(act.toJson(null, null, act.getOrder(), false));
          break;
        case 1:
          // check for valid dir objs
          List<String> strongDobjs = new ArrayList<String>();
          List<String> weakDobjs = new ArrayList<String>();
          for (WorldObject wobj : wobjs) {
            if (wobj.affordsDobj(act) == Afforded.STRONGLY) {
              strongDobjs.add(wobj.getName());
            }else if (wobj.affordsDobj(act) == Afforded.WEAKLY){
              weakDobjs.add(wobj.getName());
            }
          }
          affords.append(act.toJson(strongDobjs, null, act.getOrder(), false));
          affords.append(act.toJson(weakDobjs, null, act.getOrder(), true));
          break;
        case 2:
          // check for valid dir objs first
          for (WorldObject wobj : wobjs) {
            if (wobj.affordsDobj(act).isAfforded()) {
              boolean weak = (wobj.affordsDobj(act) == Afforded.WEAKLY);
              List<String> dobj = Collections.singletonList(wobj.getName());
              List<String> strongIobjs = new ArrayList<String>();
              List<String> weakIobjs = new ArrayList<String>();
              for (WorldObject iobj : wobjs) {
                if (iobj.affordsIobj(act) == Afforded.STRONGLY) {
                  strongIobjs.add(iobj.getName());
                }else if (iobj.affordsIobj(act) == Afforded.WEAKLY) {
                  weakIobjs.add(iobj.getName());
                }
              }
//CONT: test affordance construction for weakness and order
              affords.append(act.toJson(dobj, strongIobjs, act.getOrder(), weak));
              affords.append(act.toJson(dobj, weakIobjs, act.getOrder(), true));
            }
          }
          break;
      }
    }
    if (affords.charAt(affords.length() - 2) == ',') {
      affords.deleteCharAt(affords.length() - 2);
    }
    affords.append("]");
    return affords.toString();
  }

  public static String getHeader() {
    return "<script class='header'></script>";
  }
  
  public static String getFooter() {
    return "<script class='footer'>" +
      "{" +
        "\"affordances\": " + getAffordances() + ",\n" +
        "\"objects\": " + getObjects() + "\n" + 
      "}" + 
      "</script>";
  }

  /** 
   * Returns a JSON list of all objects that currently support 
   * affordances. 
   */
  public static String getObjects() {
    List<String> objNames = new ArrayList<String>(objs.keySet());
    //surround each in quotes
    for (int i = 0; i < objNames.size(); i++) {
      objNames.set(i, "\"" + objNames.get(i) + "\"");
    }
    return objNames.toString();
  }

  public static String init() {
    objs.clear();
    pc = new PlayerCharacter();
    objs.put(pc.getName(), pc);
    Ball ball = new Ball();
    objs.put(ball.getName(), ball);
    Dog dog = new Dog();
    objs.put(dog.getName(), dog);
        
    return p("You are standing in a vacant lot.  A " + 
      dog.toString("small dog") + " with a " + 
      ball.toString("red ball") + " in its mouth is watching you.");
  }
  
  /**
   * Returns the given text wrapped in a &lt;p&gt;...&lt;/p&gt; 
   */
  public static String p(String text) {
    return "<p>" + text + "</p>";
  }
}
